//DeCompression of .huff files
#include "unhuff.h"

/********************************************
 *   readFile() takes in an character string
 *   that is the filename specified as a 
 *   command-line argument by the user, 
 *   validats that it is a .huff file, and
 *   returns a file pointer to the opened 
 *   file.  If the file is invalid, it returns
 *   an error message and quits the program.
 *
 *   Author: John Jachna
 ********************************************/

FILE* readFileU(char* filename)
{
	FILE* fp;
	int i;
	int done = 0;
	int sLength = strlen(filename);
		
	for(i = 0; i < sLength; i++)
	{
		if(filename[i] == '.')
		{
			while(done == 0)
			{
				if((sLength - 1 - i) != 4) break;
			
				if(filename[i+1] == 'h' && filename[i+2] == 'u' && filename[i+3] == 'f' && filename[i+4] == 'f')
				{
					fp = fopen(filename, "r");
					return fp;
				}
				done = 1;
			}
		}
	}

    return NULL;
}
/***********************************************
*
* getData reads in a huff file header and creates
* a tree out of it. 
*
* Author: Michael Sickles, John Jachna
*
***********************************************/
huffTree* getData(FILE *fp,huffTree* root,long* amount)
{
        char line[9];//read in the header file line
        char buff[100];
        char code[100];//code bits
        char c;
        int bitlen;
        //char bit;
        int i,j;
        huffTree* node;//store temp info
        huffTree* parent,*child;//used to create tree
        fgets(line,sizeof(line),fp);//read in header
        if(strcmp(line,"--huff--")){
            //if it doesnt start with --huff-- then not
            //made by our program.
            printf("Error: not compressed using corresponding program.\n");
            exit(ERROR);
        }
        //loop header
        while(1){
            //first read in character
            c = fgetc(fp);
            //then read in rest of line
            fgets(code,100,fp);
            //if two new lines are at beginning by error, read in again
            if(c == '\n' && code[0] == '\n')fgets(code,100,fp);
            //if a new line and dash appear, header is done being read
            if(c=='\n' && code[0] == '-' ){
            i=1;
            //so convert the -code to just a long number containting amount of 
            //bits
            while(code[i] != '\n' && i<(int)strlen(code)-1){
    
                buff[i-1]=code[i];
                i++;
            }
            buff[i]='\0';
            *amount = atol(buff);
            return root;
        } 
        //save the length of code string
        bitlen =(int)strlen(code)-1;
        //and then a temp node
        node = malloc(sizeof(huffTree));
        node -> character = c;
        node -> code = code;
        node->right =NULL;
        node->left = NULL;
        //create a parent
        parent = malloc(sizeof(huffTree));
        parent = root;

        /* read in each bit and traverse a tree until all bits are read. 
           store a node at final location */
        for(j=0;j<=bitlen;j++){
            if(j == (bitlen-1)){ 
                //if you reached the end, tack on node
                if(code[j]=='1'){
                    parent->right = node;
                }else{
                    parent->left = node;
            }
            }else{
                //create a child
                child = malloc(sizeof(huffTree));
                if(code[j]=='1'){ //go right
                   /*if child already exists, then free it up and move down the tree 
                    other wise tack on the child node*/
                    if(parent->right == NULL){
                    parent->right = child;
                    parent=child;
                    }else{
                        parent=parent->right;
                        free(child);
                    }
                }else{ //go left
                   /*if child already exists, then free it up and move down the tree 
                    otherwise tack on child node */
                    if(parent->left == NULL){
                    parent->left = child;
                    parent = child;
                    }else{
                        parent=parent->left;
                        free(child);
                    }
                }
            }
        }
        
        }
      return root; //send back grand pappa root

}

/************************************
*
* This functions Decodes the .huff file
* and stores the binary conversion into
* a temporary file to be read in later
*
* Author: John Jachna
*
**********************************/

void decodeFromFile(FILE* fin)
{
	int c;
	FILE* fout;
	char buffer[9];
	int i, next, bit;
//create a bit buffer	
	buffer[8] = '\0';
	fout = fopen("uhtemp.temp", "w");
	
	//read in huff file
	while((c = fgetc(fin)) != EOF)
	{
               //convert character back to binary
		i = (int)c;
		next = i % 128;
		bit = i / 128;
		buffer[0] = bit;
		i = next;
		
		next = i % 64;
		bit = i / 64;
		buffer[1] = bit;
		i = next;
		
		next = i % 32;
		bit = i / 32;
		buffer[2] = bit;
		i = next;
		
		next = i % 16;
		bit = i / 16;
		buffer[3] = bit;
		i = next;
		
		next = i % 8;
		bit = i / 8;
		buffer[4] = bit;
		i = next;
		
		next = i % 4;
		bit = i / 4;
		buffer[5] = bit;
		i = next;
		
		next = i % 2;
		bit = i / 2;
		buffer[6] = bit;
		i = next;
		
		next = i % 1;
		bit = i / 1;
		buffer[7] = bit;
		
	        //store binary
                fprintf(fout, "%d%d%d%d%d%d%d%d", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
	}
        fclose(fout);
}

/*********************************************
*
*  decode takes in a bit sequence and decodes
*  the huffTree printing to a file with
*  extenstion .unhuff
*
* Author Michael Sickles
*
*******************************************/
void decode(char* filename,huffTree* root,long amount){
    FILE* fout;
    char* fileout;
    huffTree* parent;
    int ret,i;
    char bit;
    FILE* ftemp;

    ftemp = fopen("uhtemp.temp","r");
       
    //create space for output name
    fileout = malloc((strlen(filename)+7)*sizeof(char));
    //create the output name filename.unhuff
    ret = sprintf(fileout,"%s.unhuff",filename);
    //open it up for writing
    fout = fopen(fileout,"w+");
    //start at the root
    parent = root;
    for(i=0;i<=amount;i++){
        bit = fgetc(ftemp);
        //if its childrent are both null, write character to file
        if(parent->left == NULL && parent ->right == NULL){
            fprintf(fout,"%c",parent->character);
            parent = root;//go back to the root
        }
        //if read in a 1 go right
        if(bit=='1'){
                parent = parent->right;
        }else{
            //other wise you go left
            parent = parent->left;
        }
            


    }

//close file

  fclose(fout);
fclose(ftemp);

}
/*------------------------------------------------------*/

int main(int argc, char* argv[])
{
	char* filename;
	FILE* fp;

        long amount;
            
    huffTree* root;
    //create root
    root = malloc(sizeof(huffTree));
    root->right = NULL;
    root->left =NULL;
	if(argc != 2)
	{
		printf("Usage: ./unhuff <filename.huff>\n");
		return ERROR;
	}
	
	filename = argv[1];
	fp = readFileU(filename);
	
	if(fp == NULL)
	{
		printf("Error: unable to read file \"%s\".  Please ensure that the specified file was created using the program \"huff.c\" and ends in \".huff\"\n", filename);
		return ERROR;
	}
	
    root=getData(fp,root,&amount);
    decodeFromFile(fp);
    decode(filename,root,amount);
    //remove temp file
    remove("uhtemp.temp");
	return OK;
}
